#define XSTEP 3
#define XDIR 4
#define YSTEP 5
#define YDIR 6
#define ENABLE 8
#define LASER 13

#define AUTO_DISABLE_MS 2000
#define XSTEP_PER_PIXEL 40
#define YSTEP_PER_PIXEL 40
#define X_INTERVAL_MS 6//pixel burning time = X_INTERVAL_MS * XSTEP_PER_PIXEL
#define Y_INTERVAL_MS 2//
#define MIN_BRIGHTNESS 128

char command[32];
int commandCount=0;
int noData=0;

int height=0,width=0;

void setup()
{
  Serial.begin(9600);
  initCommand();
  initSteppers();
  Serial.println("ready");
}
void loop()
{
  char buff;
  if(Serial.available()>0)
  {
    buff=Serial.read();
    noData=0;
    if(buff != '\n')
    {
      command[commandCount]=buff;
      commandCount++;
    }
  }
  else
  {
    noData++;
    delayMicroseconds(100);
  }
  if(commandCount && buff=='\n')
  {
    runCommand(command,commandCount);
    initCommand();
  }
  if(noData>AUTO_DISABLE_MS*10)
  {
    noData=0;
    disableSteppers();
  }
}
void initCommand()
{
  for(int i=0;i<32;i++)
    command[i]=0;
  commandCount=0;
}
void initSteppers()
{
  pinMode(ENABLE,OUTPUT);
  pinMode(XSTEP,OUTPUT);
  pinMode(XDIR,OUTPUT);
  pinMode(YSTEP,OUTPUT);
  pinMode(YDIR,OUTPUT);
  pinMode(LASER,OUTPUT);
  disableSteppers();
}
void disableSteppers()
{
  digitalWrite(ENABLE,HIGH);
}
void enableSteppers()
{
  digitalWrite(ENABLE,LOW);
}

boolean hasKey(char key,char cmd[],int cmdsize)
{
  for(int i=0;i<cmdsize;i++)
    if(cmd[i]==key)
      return true;
  return false;
}

double getValue(char key,char cmd[],int cmdsize)
{
  char temp[10]="";
  for(int i=0;i<cmdsize;i++)
  {
    if(cmd[i]==key)
    {
      i++;
      int k=0;
      while(i<cmdsize && k<10)
      {
        if(cmd[i]==0||cmd[i]==' ')
          break;
        temp[k]=cmd[i];
        i++;
        k++;
      }
      return strtod(temp,NULL);
    }
  }
  return 0;
}
void setLaser(byte brightness)
{
  analogWrite(LASER,brightness);
}
void doStep(byte pin)
{
  digitalWrite(pin,HIGH);
  delayMicroseconds(5);
  digitalWrite(pin,LOW);
}
void doX()
{
  for(int i=0;i<XSTEP_PER_PIXEL;i++)
  {
    doStep(XSTEP);
    delay(X_INTERVAL_MS);
  }
}
void doX_fast()
{
  for(int i=0;i<XSTEP_PER_PIXEL;i++)
  {
    doStep(XSTEP);
    delayMicroseconds(200);
  }
}
void doY()
{
  for(int i=0;i<YSTEP_PER_PIXEL;i++)
  {
    doStep(YSTEP);
    delay(Y_INTERVAL_MS);
  }
}

void runCommand(char cmd[],int cmdsize)
{
  if(cmd[0]=='/')
    return;
  if(hasKey('C',cmd,cmdsize))
  {
    enableSteppers();
    if(width)
    {
      
      setLaser((byte)map(getValue('C',cmd,cmdsize),0,255,255,MIN_BRIGHTNESS));
      digitalWrite(XDIR,HIGH);
      doX();
    }
    else
    {
      Serial.println("W?");
    }
    Serial.println("ok");
    return;
  }
  if(hasKey('L',cmd,cmdsize))
  {
    enableSteppers();
    if(height)
    {
      digitalWrite(YDIR,HIGH);
      doY();
      digitalWrite(XDIR,LOW);
      setLaser(0);
      for(int i=0;i<width;i++)
        doX_fast();
    }
    else
    {
      Serial.println("H?");
    }
    Serial.println("ok");
    return;
  }
  if(hasKey('H',cmd,cmdsize))
  {
    height=(int)getValue('H',cmd,cmdsize);
    Serial.println("ok");
    return;
  }
  if(hasKey('W',cmd,cmdsize))
  {
    width=(int)getValue('W',cmd,cmdsize);
    Serial.println("ok");
    return;
  }
  if(hasKey('E',cmd,cmdsize))
  {
    enableSteppers();
    digitalWrite(YDIR,LOW);
    for(int i=0;i<height;i++)
      doY();
    Serial.println("ok");
    return;
  }
}
